package edu.neu.cs6515.player.strategy;

import java.util.List;
import java.util.Map.Entry;

import edu.neu.cs6515.board.BoardCoord;
import edu.neu.cs6515.board.HexagonBoard;
import edu.neu.cs6515.util.Pair;
import edu.neu.cs6515.xml.IngeniousColor;
import edu.neu.cs6515.xml.Placement;
import edu.neu.cs6515.xml.Score;
import edu.neu.cs6515.xml.Tile;

/**
 * Strategy #2: "Lowest score first"
 * 
 * - Enumerate all possible placements (use getCurrentBoardState() in proxy to
 * get the information about the already occupied hexagons) - For each placement
 * try each tile - Find the placement to get the highest points for lowest
 * scored color - If it is not possible to score in the player's lowest color,
 * then try the next lowest score by the same strategy
 */
public class LowestColorFirstStrategy extends AbstractStrategy {

    @Override
    public Placement getNextMove(final List<Tile> tilesInHand,
            final int numberOfPlayers, final List<Placement> placementsSofar,
            final Score scoreBeforePlacement) {
        HexagonBoard scoreTestBoard = new HexagonBoard(numberOfPlayers);
        List<Pair<BoardCoord>> possiblePlacements = getAllPossiblePlacements(
                placementsSofar, scoreTestBoard);
        if (possiblePlacements.size() == 0) {
            throw new StrategyException("no more openning on the board");
        }

        return findBestPlacement(tilesInHand, scoreBeforePlacement,
                scoreTestBoard, possiblePlacements);
    }

    /**
     * @param tilesInHand
     *            tiles currently in the hand of the player
     * @param scoreBeforePlacement
     *            Score of the player before this placement
     * @param scoreTestBoard
     *            the board to be used to calculate scores
     * @param possiblePlacements
     *            the list of possible, legal placements on the board
     * @return the best placement according to this strategy
     */
    private Placement findBestPlacement(final List<Tile> tilesInHand,
            final Score scoreBeforePlacement,
            final HexagonBoard scoreTestBoard,
            final List<Pair<BoardCoord>> possiblePlacements) {

        List<Entry<IngeniousColor, Integer>> sortedScore = scoreBeforePlacement
                .getIncreasingOrderSortedScore();

        Placement bestPlacementSoFar = createPlacement(
                possiblePlacements.get(0), tilesInHand.get(0));
        int index = 0;
        boolean noPlacementFound = true;

        while (noPlacementFound && index < sortedScore.size()) {
            IngeniousColor lowestColor = sortedScore.get(index++).getKey();
            for (Pair<BoardCoord> pair : possiblePlacements) {
                for (Tile tile : tilesInHand) {
                    Placement attemptedPlacement = createPlacement(pair, tile);
                    if (currentIsBetterThanBestSoFar(scoreTestBoard,
                            attemptedPlacement, bestPlacementSoFar, lowestColor)) {
                        bestPlacementSoFar = attemptedPlacement;
                        noPlacementFound = false;
                    }
                }
            }
        }
        return bestPlacementSoFar;
    }

    /**
     * decide whether or not the attemptedPlacement is better than the
     * bestPlacementSoFar
     * 
     * @param scoreTestBoard
     *            board to place on
     * @param attemptedPlacement
     *            attempted Placement
     * @param bestPlacementSoFar
     *            best Placement so far
     * @param lowestColor
     *            the lowest color of the current score
     * @return true if the attemptedPlacement is better than the
     *         bestPlacementSoFar
     */
    private boolean currentIsBetterThanBestSoFar(
            final HexagonBoard scoreTestBoard,
            final Placement attemptedPlacement,
            final Placement bestPlacementSoFar, final IngeniousColor lowestColor) {

        Score scoreOfBestPlacementSoFar = scoreTestBoard
                .scoreAMove(bestPlacementSoFar);
        Score scoreOfAttemptedPlacement = scoreTestBoard
                .scoreAMove(attemptedPlacement);
        int highestColorIncreaseOfAttemptedPlacement = scoreOfAttemptedPlacement
                .getPointsForColor(lowestColor);
        int highestColorIncreaseSoFar = scoreOfBestPlacementSoFar
                .getPointsForColor(lowestColor);
        return highestColorIncreaseOfAttemptedPlacement > highestColorIncreaseSoFar;
    }

}
